﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace BattleforgeAssault
{
    public enum SpacingDirection
    {
        TopLeft, BottomRight
    }

    public class SpacingPanel : Panel
    {
        #region Properties

        /// <summary>
        /// Gets or sets the direction the children should space themselves (the origin)
        /// </summary>
        public SpacingDirection Orientation
        {
            get { return (SpacingDirection)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(SpacingDirection), typeof(SpacingPanel), new PropertyMetadata(SpacingDirection.TopLeft));

        /// <summary>
        /// Gets to sets the space each child should be from their sibling horizontally in pixels
        /// </summary>
        /// <remarks>
        /// Setting the value to double.PositiveInfinity will arrange the items evenly across the width of the panel
        /// </remarks>
        public double HorizontalSpacing
        {
            get { return (double)GetValue(HorizontalSpacingProperty); }
            set { SetValue(HorizontalSpacingProperty, value); }
        }
        public static readonly DependencyProperty HorizontalSpacingProperty =
            DependencyProperty.Register("HorizontalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(double.PositiveInfinity));

        /// <summary>
        /// Gets to sets the space each child should be from their sibling vertically in pixels
        /// </summary>
        /// <remarks>
        /// Setting the value to double.PositiveInfinity will arrange the items evenly across the height of the panel
        /// </remarks>
        public double VerticalSpacing
        {
            get { return (double)GetValue(VerticalSpacingProperty); }
            set { SetValue(VerticalSpacingProperty, value); }
        }
        public static readonly DependencyProperty VerticalSpacingProperty =
            DependencyProperty.Register("VerticalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(double.PositiveInfinity));

        /// <summary>
        /// Gets or sets the minimimum <see cref="HorizontalSpacing"/> for each child
        /// </summary>
        public double MinHorizontalSpacing
        {
            get { return (double)GetValue(MinHorizontalOverlapSpacing); }
            set { SetValue(MinHorizontalOverlapSpacing, value); }
        }
        public static readonly DependencyProperty MinHorizontalOverlapSpacing =
            DependencyProperty.Register("MinHorizontalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(0.0));

        /// <summary>
        /// Gets or sets the maximum <see cref="HorizontalSpacing"/> for each child
        /// </summary>
        public double MaxHorizontalSpacing
        {
            get { return (double)GetValue(MaxHorizontalSpacingProperty); }
            set { SetValue(MaxHorizontalSpacingProperty, value); }
        }
        public static readonly DependencyProperty MaxHorizontalSpacingProperty =
            DependencyProperty.Register("MaxHorizontalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(double.PositiveInfinity));

        /// <summary>
        /// Gets or sets the minimimum <see cref="VerticalSpacing"/> for each child
        /// </summary>
        public double MinVerticalSpacing
        {
            get { return (double)GetValue(MinVerticalOverlapSpacing); }
            set { SetValue(MinVerticalOverlapSpacing, value); }
        }
        public static readonly DependencyProperty MinVerticalOverlapSpacing =
            DependencyProperty.Register("MinVerticalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(0.0));

        /// <summary>
        /// Gets or sets the maximum <see cref="VerticalSpacing"/> for each child
        /// </summary>
        public double MaxVerticalSpacing
        {
            get { return (double)GetValue(MaxVerticalSpacingProperty); }
            set { SetValue(MaxVerticalSpacingProperty, value); }
        }
        public static readonly DependencyProperty MaxVerticalSpacingProperty =
            DependencyProperty.Register("MaxVerticalSpacing", typeof(double), typeof(SpacingPanel), new PropertyMetadata(double.PositiveInfinity));

        /// <summary>
        /// Gets or sets the alignment of the childeren of the panel
        /// </summary>
        /// <remarks>
        /// This property determines how each child aligns with its siblings
        /// </remarks>
        public HorizontalAlignment HorizontalChildAlignment
        {
            get { return (HorizontalAlignment)GetValue(HorizontalChildAlignmentProperty); }
            set { SetValue(HorizontalChildAlignmentProperty, value); }
        }
        public static readonly DependencyProperty HorizontalChildAlignmentProperty =
            DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(SpacingPanel), new PropertyMetadata(HorizontalAlignment.Left));

        /// <summary>
        /// Gets or sets the alignment of the childeren of the panel
        /// </summary>
        /// <remarks>
        /// This property determines how each child aligns with its siblings
        /// </remarks>
        public VerticalAlignment VerticalChildAlignment
        {
            get { return (VerticalAlignment)GetValue(VerticalChildAlignmentProperty); }
            set { SetValue(VerticalChildAlignmentProperty, value); }
        }
        public static readonly DependencyProperty VerticalChildAlignmentProperty =
            DependencyProperty.Register("VerticalChildAlignment", typeof(VerticalAlignment), typeof(SpacingPanel), new PropertyMetadata(VerticalAlignment.Top));

        #endregion Properties

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (FrameworkElement child in Children)
            {
                child.Measure(availableSize);
            }

            if (availableSize.Height > 2000) { availableSize.Height = 2000; }
            if (availableSize.Width > 4000) { availableSize.Width = 4000; }
            
            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double x = HorizontalSpacing;
            double y = VerticalSpacing;

            // check for auto size if there is more than one child
            if (Children.Count > 1)
            {
                if (x == double.PositiveInfinity)
                {
                    if (HorizontalChildAlignment == HorizontalAlignment.Center)
                    {
                        x = (finalSize.Width - Children[Children.Count - 1].DesiredSize.Width / 2 - Children[0].DesiredSize.Width / 2) / (Children.Count - 1);
                    }
                    else if ((HorizontalChildAlignment == HorizontalAlignment.Right && Orientation == SpacingDirection.TopLeft) ||
                        (HorizontalChildAlignment == HorizontalAlignment.Left && Orientation == SpacingDirection.BottomRight))
                    {
                        x = (finalSize.Width - Children[0].DesiredSize.Width) / (Children.Count - 1);
                    }
                    else
                    {
                        x = (finalSize.Width - Children[Children.Count - 1].DesiredSize.Width) / (Children.Count - 1);
                    }
                }

                if (y == double.PositiveInfinity)
                {
                    if (VerticalChildAlignment == VerticalAlignment.Center)
                    {
                        y = (finalSize.Height - Children[Children.Count - 1].DesiredSize.Height / 2 - Children[0].DesiredSize.Height / 2) / (Children.Count - 1);
                    }
                    else if ((VerticalChildAlignment == VerticalAlignment.Bottom && Orientation == SpacingDirection.TopLeft) ||
                        (VerticalChildAlignment == VerticalAlignment.Top && Orientation == SpacingDirection.BottomRight))
                    {
                        y = (finalSize.Height - Children[0].DesiredSize.Height) / (Children.Count - 1);
                    }
                    else
                    {
                        y = (finalSize.Height - Children[Children.Count - 1].DesiredSize.Height) / (Children.Count - 1);
                    }
                }
            }

            // check min/max
            if (x < MinHorizontalSpacing) { x = MinVerticalSpacing; }
            if (x > MaxHorizontalSpacing) { x = MaxHorizontalSpacing; }
            if (y < MinVerticalSpacing) { y = MinVerticalSpacing; }
            if (y > MaxVerticalSpacing) { y = MaxVerticalSpacing; }

            // define starting location and interval
            Point location;
            if (Orientation == SpacingDirection.TopLeft)
            {
                location = new Point(0, 0);
            }
            else
            {
                location = new Point(finalSize.Width - Children[0].DesiredSize.Width, finalSize.Height - Children[0].DesiredSize.Height);
                x = -x;
                y = -y;
            }

            // layout each child
            for (int i = 0; i < Children.Count; ++i)
            {
                Children[i].Arrange(new Rect(location, Children[i].DesiredSize));

                // setup next location
                location.X += x;
                location.Y += y;

                location = AlignWithNext(location, i);
            }

            return finalSize;
        }

        private Point AlignWithNext(Point location, int i)
        {
            // modify location based on alignment
            if (HorizontalChildAlignment == HorizontalAlignment.Center && i < Children.Count - 1)
            {
                location.X += Children[i].DesiredSize.Width / 2;
                location.X -= Children[i + 1].DesiredSize.Width / 2;
            }
            else if (HorizontalChildAlignment == HorizontalAlignment.Right && i < Children.Count - 1)
            {
                location.X += Children[i].DesiredSize.Width;
                location.X -= Children[i + 1].DesiredSize.Width;
            }

            if (VerticalChildAlignment == VerticalAlignment.Center && i < Children.Count - 1)
            {
                location.Y += Children[i].DesiredSize.Height / 2;
                location.Y -= Children[i + 1].DesiredSize.Height / 2;
            }
            else if (VerticalChildAlignment == VerticalAlignment.Bottom && i < Children.Count - 1)
            {
                location.Y += Children[i].DesiredSize.Height;
                location.Y -= Children[i + 1].DesiredSize.Height;
            }
            return location;
        }
    }
}
